package com.hipmarket.platform.security

import com.fasterxml.jackson.databind.ObjectMapper
import com.hipmarket.platform.security.exception.HttpSecurityException
import com.hipmarket.platform.security.exception.InvalidTokenException
import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.core.AuthenticationException
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.util.AntPathMatcher
import org.springframework.web.filter.OncePerRequestFilter
import javax.servlet.FilterChain
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

class JwtAuthenticationFilter(private val manager: AuthenticationManager, private val permitUrls: List<String>): OncePerRequestFilter() {

    private val objectMapper = ObjectMapper()

    private val antPathMatcher = AntPathMatcher()

    override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
        SecurityContextHolder.clearContext()
        val required = !isPermitUrl(request.requestURI)
        try {
            securityAuthentication(request, required)
        } catch (e: AuthenticationException) {
            if (required) {
                return this.onAuthenticationFailure(e, response)
            }
        } catch (e: HttpSecurityException) {
            if (required) {
                return this.onAuthenticationFailure(e, response)
            }
        }
        filterChain.doFilter(request, response)
    }

    private fun securityAuthentication(request: HttpServletRequest, required: Boolean) {
        val authorizationToken = request.getHeader("Authorization").orEmpty()
        if (!authorizationToken.startsWith("Bearer ") && required) {
            throw InvalidTokenException()
        }

        val token = if (authorizationToken.length > 10) authorizationToken.substring(7) else ""
        val authentication = JwtAuthenticationToken(token, required)
        manager.authenticate(authentication)
    }

    private fun onAuthenticationFailure(e: Exception, response: HttpServletResponse) {
        val responseBody = objectMapper.writeValueAsString(mapOf("errMsg" to e.message))
        response.contentType = "Application/json"
        response.characterEncoding = "utf-8"
        response.status = 401
        response.writer.print(responseBody)
    }

    private fun isPermitUrl(uri: String): Boolean {
        return permitUrls.any { antPathMatcher.match(it, uri) }
    }


}